use core::marker::PhantomData;
use periph_utils::stm32f1;
use cmsis_rtos2_rs::os_delay;
use periph_utils::GpioConfig;

use crate::cp_weight::{driver::APP_INFO, WeightErr};

#[derive(GpioConfig)]
#[gpio(init = "gpio_init",
    io(name = "h1_data", io = "PB10", mode = "IN_FLOAT", read),
    io(name = "h1_sck", io = "PB11", mode = "OUT_PP", set, reset),
    io(name = "h2_data", io = "PB6", mode = "IN_FLOAT", read),
    io(name = "h2_sck", io = "PB7", mode = "OUT_PP", set, reset)
)]
struct Pin;

pub unsafe fn init() {
    Pin::gpio_init();
}

pub fn adjust1(adj_val: u32) -> Result<(), WeightErr> {
    type H = Hx711<H1>;
    let mut app_info = APP_INFO.lock();

    let value = H::read()?;

    if adj_val == 0x00 {
        app_info.offset1 = value;
    } else {
        app_info.k1 = ( value - app_info.offset1 ) / adj_val;
    }
    app_info.store().map_err(WeightErr::Store)?;
    
    Ok(())
}

pub fn adjust2(adj_val: u32) -> Result<(), WeightErr> {
    type H = Hx711<H2>;
    let mut app_info = APP_INFO.lock();

    let value = H::read()?;

    if adj_val == 0x00 {
        app_info.offset2 = value;
    } else {
        app_info.k2 = ( value - app_info.offset2 ) / adj_val;
    }
    app_info.store().map_err(WeightErr::Store)?;
    
    Ok(())
}

pub fn read1() -> Result<u32, WeightErr> {
    type H = Hx711<H1>;
    let app_info = APP_INFO.lock();
    let value = H::read()?;
    if app_info.offset1 > value {
        Ok(0)
    } else {
        Ok((value - app_info.offset1) / app_info.k1)
    }
}

pub fn read2() -> Result<u32, WeightErr> {
    type H = Hx711<H2>;
    let app_info = APP_INFO.lock();
    let value = H::read()?;
    if app_info.offset2 > value {
        Ok(0)
    } else {
        Ok((value - app_info.offset2) / app_info.k2)
    }
}

struct H1;
struct H2;

impl Hx711Ctrl for H1 {
    fn data_read() -> bool {
        Pin::h1_data_read()
    }
    fn sck_clr() {
        Pin::h1_sck_set();
    }
    fn sck_set() {
        Pin::h1_sck_reset();
    }
    const TIMEOUT_ERR: WeightErr = WeightErr::Hx1ReadTimeout;
}

impl Hx711Ctrl for H2 {
    fn data_read() -> bool {
        Pin::h2_data_read()
    }
    fn sck_clr() {
        Pin::h2_sck_reset();
    }
    fn sck_set() {
        Pin::h2_sck_set();
    }
    const TIMEOUT_ERR: WeightErr = WeightErr::Hx2ReadTimeout;
}

trait Hx711Ctrl {
    
    fn sck_set();
    fn sck_clr();
    fn data_read() -> bool;

    const TIMEOUT_ERR: WeightErr;
    const TIME_DURATION: u32 = 5 * 1000;
}

struct Hx711<T: Hx711Ctrl> {
    _m: PhantomData<T>,
}

impl <T: Hx711Ctrl> Hx711<T> {

    fn read_impl() -> u32 {
        let mut value = 0;
        for _ in 0..24 {
            T::sck_set();
            value = value << 1;
            T::sck_clr();
            if T::data_read() {
                value += 1;
            }
        }
        T::sck_set();
        T::sck_clr();
        (value ^ 0x800000) * 10
    }

    fn read() -> Result<u32, WeightErr> {
        let mut time_count = 0;
        while T::data_read() {
            time_count += 10;
            os_delay(10);
            if time_count >= T::TIME_DURATION {
                return Err(T::TIMEOUT_ERR);
            }
        }
        Ok( Self::read_impl() )
    }
}


